#ifndef _TYPE_LIST_HPP_
#define _TYPE_LIST_HPP_

#include <type_traits>

namespace Bamboo
{
	template<typename... TYPE>
	struct type_list
	{
	};

	typedef type_list<> NullList;

	//size
	template<typename... TYPE> struct size;

	template<typename... TYPE>
	struct size<type_list<TYPE...>> : std::integral_constant<int, sizeof... (TYPE)>
	{
	};

	//first_type
	template<typename T, typename... TYPE> struct first_type;

	template<typename T, typename... TYPE>
	struct first_type<type_list<T,TYPE...>>
	{
		typedef T type;
	};

	//last_type
	template<typename T ,typename... TYPE> struct last_type;

	template <typename T>
	struct last_type<type_list<T>>
	{
		typedef T type;
	};

	template <typename T, typename ...TYPE>
	struct last_type<type_list<T, TYPE...>> : last_type<type_list<TYPE...>>
	{
	};

	//push_front
	template<typename T, typename... TYPE> struct push_front;

	template<typename T, typename... TYPE>
	struct push_front<T, type_list<TYPE...>>
	{
		typedef type_list<T, TYPE...>  type;
	};

	//pop_front
	template<typename... TYPE> struct pop_front;

	template<typename T, typename... TYPE>
	struct pop_front<type_list<T, TYPE...>>
	{
		typedef type_list<TYPE...> type;
	};

	template<>
	struct pop_front<NullList>
	{
		typedef NullList type;
	};

	//at
	template<unsigned int N, typename... TYPE> struct at;

	template<unsigned int N, typename T, typename... TYPE>
	struct at<N, type_list<T, TYPE...>>
	{
		typedef typename at<N - 1, type_list<TYPE...>>::type type;
	};

	template<typename T, typename... TYPE>
	struct at<0, type_list<T, TYPE...>>
	{
		typedef T type;
	};

	template<>
	struct at<0, NullList>
	{
		typedef NullList type;
	};

	//indexof
	template<typename T, typename... TYPE> struct indexof;

	template<typename T, typename H, typename... TYPE>
	struct indexof<T, type_list<H, TYPE...>>
	{
		enum { value = (indexof<T, type_list<TYPE...>>::value == -1) ? -1 : indexof<T, type_list<TYPE...>>::value + 1 };
	};

	template<typename T, typename... TYPE>
	struct indexof<T, type_list<T, TYPE...>>
	{
		enum { value = 0 };
	};

	template<typename T>
	struct indexof<T, NullList>
	{
		enum { value = -1 };
	};

	//concat
	template<typename A, typename B> struct concat;

	template<typename... TYPEA, typename... TYPEB>
	struct concat<type_list<TYPEA...>, type_list<TYPEB...>>
	{
		typedef type_list<TYPEA..., TYPEB...> type;
	};

	template<typename T, typename... TYPE>
	struct concat<type_list<TYPE...>, T>
	{
		typedef type_list<TYPE..., T> type;
	};

	template<typename T, typename... TYPE>
	struct concat<T, type_list<TYPE...>>
	{
		typedef type_list<T, TYPE...>  type;
	};

	//erase_first_of
	template<typename T, typename... TYPE> struct erase_first_of;

	template<typename T, typename H, typename... TYPE>
	struct erase_first_of<T, type_list<H, TYPE...>>
	{
		typedef typename concat<H, typename erase_first_of<T, type_list<TYPE...>>::type>::type type;
	};

	template<typename T, typename... TYPE>
	struct erase_first_of<T, type_list<T, TYPE...>>
	{
		typedef type_list<TYPE...> type;
	};

	template<typename T>
	struct erase_first_of<T, NullList>
	{
		typedef NullList type;
	};

	//erase_all
	template<typename T, typename... TList> struct erase_all;

	template<typename T, typename H, typename... TYPE>
	struct erase_all<T, type_list<H, TYPE...>>
	{
		typedef typename concat<H, typename erase_all<T, type_list<TYPE...>>::type>::type type;
	};

	template<typename T, typename... TYPE>
	struct erase_all<T, type_list<T, TYPE...>>
	{
		typedef typename erase_all<T, type_list<TYPE...>>::type type;
	};

	template<typename T>
	struct erase_all<T, NullList>
	{
		typedef NullList type;
	};

	//reverse
	template<typename T, typename...TYPE> struct reverse;

	template<typename T, typename...TYPE>
	struct reverse<type_list<T, TYPE...>>
	{
		typedef typename concat<typename reverse<type_list<TYPE...>>::type, T>::type type;
	};

	template<>
	struct reverse<NullList>
	{
		typedef NullList type;
	};
}

#endif